/*
 * app_lamp.c
 *
 *  Created on: 2018年5月6日
 *      Author: 0yfj0
 */

#include "app_lamp.h"

#include <string.h>
#include <time.h>
#include <ctype.h>

#include "ble_lamp.h"
#include "board.h"
#include "sys.h"
#include "sys_env2.h"
#include "sys_mem.h"
#include "sys_utils.h"
#include "boards.h"
#include "nrf_error.h"
#include "app_error.h"
#include "nrf_gpio.h"
#include "ble_gap.h"
#include "peer_manager.h"
#include "app_timer.h"

/*----------------------------------------------------------------------------*/
lamp_t app;
static char sys_heap[512];
APP_TIMER_DEF(timer_id_t1s);

int lamp_strcmp(void *buf, char *str);
int lamp_kv_int(char *s, char *delim, char *delim_v, int *val, int size);
int lamp_conf_ctrl(int lamp, int *val, int len);
int lamp_conf_range(int lamp, int idx, int *val, int len);
int lamp_conf_edge(int lamp, int idx, int *val, int len);
/*----------------------------------------------------------------------------*/
void lamp_env_init(void)
{
	int i;
	int no;
	int area;
	uint32_t err_code;
	uint8_t size;

	err_code = mem_init(sys_heap, sizeof(sys_heap));
	APP_ERROR_CHECK(err_code);

	memset(&sys, 0, sizeof(sys));
	no = SYS_ENV2_NO_PARAMS;
	area = SYS_ENV2_PARAMS1A;
	size = sizeof(sys_conf_t);
	if (env2_load(no, area, &sys.conf, &size) != ENV2_ERR_OK
			|| size != sizeof(sys.conf))
	{
		snprintf(sys.conf.dev_name, sizeof(sys.conf.dev_name), DEVICE_NAME);
		sys.conf.passwd = 123456;
		env2_save(no, area, &sys.conf, sizeof(sys.conf));
	}

	memset(&app, 0, sizeof(app));
	no = SYS_ENV2_NO_POWERON;
	area = SYS_ENV2_POWERONA;
	size = sizeof(sizeof(sys.poweron));
	if (env2_load(no, area, &sys.poweron, &size) != ENV2_ERR_OK
			|| size != sizeof(sys.poweron))
	{
		sys.poweron = 1;
	}
	else
		++sys.poweron;
	env2_save(no, area, &sys.poweron, sizeof(sys.poweron));

	no = SYS_ENV2_NO_RUNPARAS;
	area = SYS_ENV2_RP_TOTALRUNA;
	size = sizeof(sys.total_run);
	if (env2_load(no, area, &sys.total_run, &size) != ENV2_ERR_OK
			|| size != sizeof(sys.total_run))
	{
		env2_save(no, area, &sys.total_run, sizeof(sys.total_run));
	}
	for (i = 0; i < LAMP_SZ; ++i)
	{
		no = SYS_ENV2_NO_L;
		size = sizeof(app.stat.lamp[0]);
		if (env2_load(no, i, &app.stat.lamp[i], &size) != ENV2_ERR_OK
				|| size != sizeof(app.stat.lamp[0]))
		{
			env2_save(no, i, &app.stat.lamp[i], sizeof(app.stat.lamp[0]));
		}

		no = SYS_ENV2_NO_RUNPARAS;
		size = sizeof(app.stat.run[0]);
		if (env2_load(no, i, &app.stat.run[i], &size) != ENV2_ERR_OK
				|| size != sizeof(app.stat.run[0]))
		{
			env2_save(no, i, &app.stat.run[i], sizeof(app.stat.run[0]));
		}
	}

	no = SYS_ENV2_NO_TIME;
	area = SYS_ENV2_TIMEA;
	size = sizeof(sys.sec);
	env2_load(no, area, &sys.sec, &size);

	err_code = app_timer_create(&timer_id_t1s, APP_TIMER_MODE_REPEATED,
			lamp_timer1s_timeout_handler);
	APP_ERROR_CHECK(err_code);
}

int lamp_init(void)
{
	uint8_t area;
	uint8_t size;
	int i;
	int no;
	int len;
	uint32_t err_code;
	uint8_t did[8];
	uint8_t conf_did[SYS_CONF_DID_SZ];
	ble_gap_addr_t addr;

	nrf_gpio_cfg_output(22);

	memset(did, 0, sizeof(did));
	memset(&addr, 0, sizeof(addr));
	err_code = sd_ble_gap_address_get(&addr);
	APP_ERROR_CHECK(err_code);
	len = sizeof(did);
	if (len > sizeof(addr))
		len = sizeof(addr);
	memcpy(did, &addr, len);

	if (sys_conf_read(SYS_CONF_DID_OFF, conf_did, SYS_CONF_DID_SZ) != 0)
	{
		sys_conf_write(SYS_CONF_DID_OFF, conf_did, SYS_CONF_DID_SZ);
	}

	for (i = 0, no = SYS_ENV2_NO_LAMP1CONF; i < LAMP_SZ; ++i, ++no)
	{
		area = SYS_ENV2_LAMPCONF_CTRLA;
		size = sizeof(app.conf[i].ctrl);
		env2_load(no, area, &app.conf[i].ctrl, &size);

		area = SYS_ENV2_LAMPCONF_RANGEA;
		size = sizeof(app.conf[i].range);
		env2_load(no, area, &app.conf[i].range, &size);

		area = SYS_ENV2_LAMPCONF_EDGEA;
		size = sizeof(app.conf[i].edge);
		env2_load(no, area, &app.conf[i].edge, &size);

		lamp_event_reset();
	}
	return 0;
}

/*
 * buf - ctrl,lamp,idx=...
 * range,lamp,idx=...
 * edge,lamp,idx=...
 * */
int lamp_conf_parse(char *buf, char *exp, int *lamp, int *idx)
{
	char chr;
	int i;
	int off;
	char num[8];
	char *argv[2];

	if (!buf || !exp || !lamp || !idx)
		return -1;

	off = strlen(exp);
	if (memcmp(buf, exp, off) != 0)
		return -1;

	off += 1;
	for (i = 0; i < sizeof(num) - 1; ++i)
	{
		chr = buf[off + i];
		if (isdigit(chr) || chr == ',')
			num[i] = chr;
		else if (chr == '=')
			break;
	}
	num[i] = '\0';

	if (char_argv(num, i, ",", argv, 2) != 2)
		return -1;

	if (char_to_bin(argv[0], strlen(argv[0]), lamp) != 0
			|| char_to_bin(argv[1], strlen(argv[1]), idx) != 0)
		return -1;
	return 0;
}

/*
 * ctrl,l,idx=vox,ir,delay_close
 * range,l,idx=enable,once,vox,ir,start,end
 * edge,l,idx=on/off,enable,once,edge
 * name=...
 * passwd=...
 * */
int lamp_conf(void *buf, uint16_t len)
{
	int l;
	int n;
	int idx;
	int val[8];
	char buff[32];
	char resp[32];
	kv_t kv;

	memset(buff, 0, sizeof(buff));
	memset(&kv, 0, sizeof(kv));
	memcpy(buff, buf, len);
	snprintf(resp, sizeof(resp), "error");
	if (lamp_conf_parse(buff, "ctrl", &l, &idx) == 0)/* ctrl,l,idx=vox,ir,delay_close */
	{
		n = lamp_kv_int(buff, "=", ",", val, 4);
		if (n > 0 && lamp_conf_ctrl(l, val, n) == 0)
		{
			snprintf(resp, sizeof(resp), "%s", (char*) buf);
		}
	}
	else if (lamp_conf_parse(buff, "range", &l, &idx) == 0)/* range,l,idx=enable,once,vox,ir,start,end */
	{
		n = lamp_kv_int(buff, "=", ",", val, 6);
		if (n > 0 && lamp_conf_range(l, idx, val, n) == 0)
		{
			snprintf(resp, sizeof(resp), "%s", (char*) buf);
		}
	}
	else if (lamp_conf_parse(buff, "edge", &l, &idx) == 0)/* edge,l,idx=on/off,enable,once,edge */
	{
		n = lamp_kv_int(buff, "=", ",", val, 4);
		if (n > 1 && lamp_conf_edge(l, idx, val, n) == 0)
		{
			snprintf(resp, sizeof(resp), "%s", (char*) buf);
		}
	}
	else if (lamp_strcmp(buff, "name") == 0)
	{
		if (char_kv(buff, "=", &kv) == 0)
		{
			memset(sys.conf.dev_name, 0, sizeof(sys.conf.dev_name));
			snprintf(sys.conf.dev_name, sizeof(sys.conf.dev_name), kv.val);
			if (env2_save(SYS_ENV2_NO_PARAMS, SYS_ENV2_PARAMS1A, &sys.conf,
					sizeof(sys.conf)) == ENV2_ERR_OK)
				snprintf(resp, sizeof(resp), "success");
		}
		else if (strcmp(buff, "name?") == 0)
		{
			snprintf(resp, sizeof(resp), "name=%s", sys.conf.dev_name);
		}
	}
	else if (lamp_strcmp(buff, "passwd") == 0)
	{
		if (lamp_kv_int(buff, "=", NULL, val, 1) == 1)
		{
			sys.conf.passwd = val[0];
			if (env2_save(SYS_ENV2_NO_PARAMS, SYS_ENV2_PARAMS1A, &sys.conf,
					sizeof(sys.conf)) == ENV2_ERR_OK)
				snprintf(resp, sizeof(resp), "success");
		}
		else if (strcmp(buff, "passwd?") == 0)
		{
			snprintf(resp, sizeof(resp), "passwd=%ld", sys.conf.passwd);
		}
	}

	ble_nus_send(resp, strlen(resp));
	return 0;
}

int lamp_startup(void)
{
	uint32_t err_code;
	uint32_t timer1s_tick;
	timer1s_tick = APP_TIMER_TICKS(1000, APP_TIMER_PRESCALER);
	err_code = app_timer_start(timer_id_t1s, timer1s_tick, NULL);
	APP_ERROR_CHECK(err_code);
	return 0;
}

int lamp_ctrl(int lamp, int state)
{
	int retval = -1;

	switch (lamp)
	{
	case 0:
	case 1:
	case 2:
		if (lamp == 0)
			nrf_gpio_pin_write(22, state ? 1 : 0);

		app.stat.lamp[lamp] = state ? 1 : 0;
		env2_save(SYS_ENV2_NO_L, SYS_ENV2_L1A + lamp, &app.stat.lamp[lamp],
				sizeof(app.stat.lamp[0]));
		if (state == 0)
		{
			env2_save(SYS_ENV2_NO_RUNPARAS, SYS_ENV2_RP_L1RUNA + lamp,
					&app.stat.run[lamp], sizeof(app.stat.run[0]));
		}
		retval = 0;
		break;
	default:
		break;
	}
	return retval;
}

void lamp_event_reset(void)
{
	int i;
	int j;

	memset(app.range_start, 0, sizeof(app.range_start));
	memset(app.range_end, 0, sizeof(app.range_end));
	memset(app.edge, 0, sizeof(app.edge));
	for (i = 0; i < LAMP_SZ; ++i)
	{
		for (j = 0; j < LAMP_RANGE_SZ; ++j)
		{
			if (app.conf[i].range[j].enable)
			{
				app.range_start[i][j] = app.conf[i].range[j].start / 100;
				app.range_start[i][j] *= 60;
				app.range_start[i][j] += app.conf[i].range[j].start % 100;
				app.range_start[i][j] *= 60;
				app.range_start[i][j] += 1;/* 防止00:00 */

				app.range_end[i][j] = app.conf[i].range[j].end / 100;
				app.range_end[i][j] *= 60;
				app.range_end[i][j] += app.conf[i].range[j].end % 100;
				app.range_end[i][j] *= 60;
				app.range_end[i][j] += 1;/* 防止00:00 */
			}
		}

		for (j = 0; j < LAMP_EDGE_SZ; ++j)
		{
			if (app.conf[i].edge[j].enable)
			{
				app.edge[i][j] = app.conf[i].edge[j].edge / 100;
				app.edge[i][j] *= 60;
				app.edge[i][j] += app.conf[i].edge[j].edge % 100;
				app.edge[i][j] *= 60;
				app.edge[i][j] += 1;/* 防止00:00 */
				if (app.edge[i][j] < sys.dsec)
					app.edge[i][j] = 0;
			}
		}
	}
}

void lamp_loop_task(void)
{
	int i;

	if (app.tbakup > LAMP_TBAKUP)
	{
		int no;
		int area;

		no = SYS_ENV2_NO_RUNPARAS;
		area = SYS_ENV2_RP_TOTALRUNA;
		env2_save(no, area, &sys.total_run, sizeof(sys.total_run));

		no = SYS_ENV2_NO_RUNPARAS;
		for (i = 0; i < LAMP_SZ; ++i)
		{
			if (app.stat.lamp[i])
				env2_save(no, i, &app.stat.run[i], sizeof(app.stat.run[0]));
		}

		if (sys.sec != sys.dsec)
		{
			no = SYS_ENV2_NO_TIME;
			area = SYS_ENV2_TIMEA;
			env2_save(no, area, &sys.sec, sizeof(sys.sec));
		}
		app.tbakup = 1;
	}
	if (sys.t10s >= 10)
	{
		sys.t10s = 0;

		struct tm *t = localtime((time_t*) &sys.sec);
		*((uint16_t*) (&app.current_time[0])) = t->tm_year + 1900;
		app.current_time[2] = t->tm_mon + 1;
		app.current_time[3] = t->tm_mday;
		app.current_time[4] = t->tm_hour;
		app.current_time[5] = t->tm_min;
		app.current_time[6] = t->tm_sec;
		app.current_time[7] = t->tm_wday;
		ble_current_time_update();
	}

	if (sys.sec != sys.dsec)
	{
		int j;

		for (i = 0; i < LAMP_SZ; ++i)
		{
			for (j = 0; j < LAMP_RANGE_SZ; ++j)
			{
				if (app.range_start[i][j])
				{
					if (app.range_start[i][j] < app.range_end[i][j])
					{
						if (app.range_start[i][j] <= sys.dsec
								&& sys.dsec <= app.range_end[i][j])
						{
							/* TODO: lamp_ctrl(i, 1); */
						}
					}
					else
					{
						if (app.range_start[i][j] <= sys.dsec
								|| sys.dsec <= app.range_end[i][j])
						{
							/* TODO: lamp_ctrl(i, 1); */
						}
					}
				}
			}

			for (j = 0; j < LAMP_EDGE_SZ; ++j)
			{
				if (app.edge[i][j] && app.edge[i][j] < sys.dsec)
				{
					lamp_ctrl(i, app.conf[i].edge[j].on);
					app.edge[i][j] = 0;
				}
			}
		}
	}
}

void lamp_timer1s_timeout_handler(void *p_context)
{
	int i;

	++sys.run;
	++sys.sec;
	if (++sys.dsec >= SYS_DAY_SEC_MAX)
	{
		sys.dsec = 0;
		lamp_event_reset();
	}

	++sys.t10s;

	++app.tbakup;
	for (i = 0; i < LAMP_SZ; ++i)
	{
		if (app.stat.lamp[i])
			++app.stat.run[i];
	}

	ble_timer1s_task();
}

/*----------------------------------------------------------------------------*/
int lamp_strcmp(void *buf, char *str)
{
	int len;

	if (!buf || !str)
		return -1;

	len = strlen(str);
	return memcmp(buf, str, len);
}

/*
 * char *s: 待解析字符串
 * char *delim: kv分割符
 * char *delim_v: 值分割符
 * int *val: 解析值保存缓存
 * int size: 解析值缓存大小(最大8)
 *
 * 返回解析到值数目
 * */
int lamp_kv_int(char *s, char *delim, char *delim_v, int *val, int size)
{
	int i;
	int n;
	int cnt;
	int bin;
	kv_t kv;
	char *argv[8];

	if (char_kv(s, delim, &kv) == 0)
	{
		cnt = 0;
		if (delim_v == NULL || size < 2)
		{
			if (char_to_bin(kv.val, strlen(kv.val), &bin) == 0)
			{
				if (val != NULL)
					*val = bin;
				cnt = 1;
			}
		}
		else
		{
			n = char_argv(kv.val, strlen(kv.val), delim_v, argv, size);
			for (i = 0; i < n; ++i)
			{
				if (char_to_bin(argv[i], strlen(argv[i]), &bin) == 0)
				{
					if (val != NULL)
						val[i] = bin;
					cnt += 1;
				}
			}
		}
		return cnt;
	}
	return -1;
}

int lamp_conf_ctrl(int lamp, int *val, int len)
{
	uint8_t area;
	uint8_t size;
	int i;
	int no;
	lamp_conf_ctrl_t *ctrl;

	if (lamp <= 0 || lamp > LAMP_SZ || val == NULL || len <= 0)
		return -1;

	i = 0;
	ctrl = &app.conf[lamp - 1].ctrl;

	ctrl->vox = val[i++];
	if (len > i)
		ctrl->ir = val[i++];
	if (len > i)
		ctrl->delay_close = val[i++];

	no = SYS_ENV2_NO_LAMP1CONF + lamp - 1;
	area = SYS_ENV2_LAMPCONF_CTRLA;
	size = sizeof(app.conf[0].ctrl);
	if (env2_save(no, area, ctrl, size) == ENV2_ERR_OK)
		return 0;
	return 0;
}

int lamp_conf_range(int lamp, int idx, int *val, int len)
{
	uint8_t area;
	uint8_t size;
	int i;
	int no;
	lamp_conf_range_t *range;

	if (lamp <= 0 || lamp > LAMP_SZ || idx < 0 || idx >= LAMP_RANGE_SZ
			|| val == NULL || len <= 0)
		return -1;

	i = 0;
	range = &app.conf[lamp - 1].range[idx];

	range->enable = val[i++];
	if (len > i)
		range->once = val[i++];
	if (len > i)
		range->vox = val[i++];
	if (len > i)
		range->ir = val[i++];
	if (len > i)
		range->start = val[i++];
	if (len > i)
		range->end = val[i++];

	no = SYS_ENV2_NO_LAMP1CONF + lamp - 1;
	area = SYS_ENV2_LAMPCONF_RANGEA;
	size = sizeof(app.conf[0].range);
	if (env2_save(no, area, range, size) == ENV2_ERR_OK)
		return 0;
	return -1;
}

int lamp_conf_edge(int lamp, int idx, int *val, int len)
{
	uint8_t area;
	uint8_t size;
	int i;
	int no;
	lamp_conf_edge_t *edge;

	if (lamp <= 0 || lamp > LAMP_SZ || idx < 0 || idx >= LAMP_EDGE_SZ
			|| val == NULL || len <= 0)
		return -1;

	i = 0;
	edge = &app.conf[lamp - 1].edge[idx];

	edge->enable = val[i++];
	if (len > i)
		edge->once = val[i++];
	if (len > i)
		edge->on = val[i++];
	if (len > i)
		edge->edge = val[i++];

	no = SYS_ENV2_NO_LAMP1CONF + lamp - 1;
	area = SYS_ENV2_LAMPCONF_EDGEA;
	size = sizeof(app.conf[0].edge);
	if (env2_save(no, area, edge, size) == ENV2_ERR_OK)
		return 0;
	return -1;
}
